in this video we're going to handle unit
selection and control in unity ECS we're
going to make various systems and
components don't listen to input and
give commands let's begin
[Music]
hello and welcome I'm your code monkey
and this channel is all about helping
you learn how to make your own games
with nf2 torrents made by a professional
indie game developer so if you find the
video helpful consider subscribing so
this is what we want to create we have a
bunch of units in our scene and they are
all just standing around
now using the mouse button I can click
and drag to draw a selection area and
all the units inside that area gets
selected as you can see we can visually
see the selection then by using the
right mouse button I can give a move
order to tell the units to go to a
certain position the positions that they
go to are dynamically generated so some
like those and as you can see they do
not overlap they all go into various
positions around our target position so
here I can select all of them tell them
all to go there and they all go into
different positions and none of them are
overlapping and everything looks good so
I can select just a bunch of them and
tell them to go there they're there and
so on I can also just click on a unit to
select each one individually so just
like this we have a very nice RTS unit
control system so this is our zone let's
get to it so here we are in our scene
and first server sir you just have a
simple entity being spawned there's no
input no controls and I can't do
anything so let's look at the code to
see how we're spawning this over here we
have our main script and down here we go
into our start we are just setting up
the camera initializing the animations
and in here we are spawning our unit we
randomized the spawn position and here
is our entity so we create an entity
archetype
first of all with marine component this
is just a simple empty tag component
then we have the translation for the
position a move to which we're going to
use in order to give our unit some
commands and then some simple animation
components so again just a basic unit
being spawned and here he is just
standing around okay crit now let's get
started and listening to our input so
we're here on our project files and
let's create a new C sharp script and
we're going to call this the unit
control system now in here we're working
with ECS so instead of a mono behavior
this will be a
component system okay so we have our
nice update function and now in here
let's do a very simple listen to our
mouse input so we do if input not get
mouse button down on the Left mouse
button so button zero so here we have
our mouse pressed and then we have in
here we have the mouse released okay so
we now know when the player presses and
releases the mouse now we're trying to
control our units so that means we need
to know the selection area so we need to
know when we started pressing the mouse
and when we release it so here let's
store the starting position so store a
private point three for the start
position and then here we set it on the
mouse world position so for that I'm
going to use a function from the code
mock utilities that you can grab for
free from unity code montcalm here I got
a function that returns the mouse run
position
okay so we have our start position that
is set when we press our mouse and now
when we release we have our end position
and now we'd start at the end we can do
some simple math in order to get our
selected area corners
so here we have our lower left and upper
right position we just do a method min
and Method Max in order to calculate it
and now with the lower left and upper
right essentially we have our selection
area so we need to do is figure out
which units are inside that area so in
here let's I go through our entities so
entities for each in here well let's
grab the translation components so we
need to do is test it there inside the
selection area so if the entity position
dot X is bigger than the lower left
so if the position is within our bounds
then let's just do a simple debugged
unlock the scene so do a debug log of
our entity all right so let's s and we
should be able to see various logs
depending on how many some entities we
have selected so here we are in here the
console if I just click on nowhere then
we see nothing but if I start in here
and let go in here yep there you go
there's our entity it was neat inside
our selection area now let's see with
two entities okay here we have two
entities starting here stop in here and
yep there's nothing started here stop
there and yep entity 1 1 start let go
and yep 0 1 and I was like both of them
and if there is only both ok awesome so
we are now correctly identifying which
entities are inside our selection area
all right now before we make a component
to actually select our units let's make
a visual to be able to see our selection
area so this won't be very simple in
here let's just make an empty game
object this will be the selection area
and now inside let's make another empty
game object this will be the sprite add
the sprite renderer component add our
simple white pixel let's tint it and
here and let's just shift the sprite to
that corner so put it on 0.5 and 0.5 so
that way the pivot right there on the
parent is on the lower left so just like
that we can easily place this transform
wherever we start and then we scale it
to reach our end position all right
that's it very simple and by default
let's start with it disabled so that
it's hidden now the first thing we need
to do is actually have a reference for
it now when using ECS we don't have
instances of our systems attached the
game objects so in here we can't just
add a serialized field
so in order to grab our reference let's
go into our main script and appear at
our field so this will be our transform
for the selection area transform
okay now in the editor just drag our
reference here's our script and just
drag the selection area okay so now we
have our reference in here obviously if
you were using this improper game you
would manage your references better but
for now just for testing this works fun
so we have our static instance which is
set on our wake so we can access this
instance in order to access our treasure
so let's go into our control system and
now the first thing we need to do is
make it visible when we have the mouse
down so in here when you start pressing
our mouse let's go into our main script
to access the instance and the selection
area transform and we just go to the
game object set active with your true
then we're going to let go we want to
hide it so we set it back into false and
now here in the middle is when we need
to actually scale it so let's you if
input dot get mouse button on the same
mouse button so this code runs one of
the mouse's hometown so in here we just
need to actually scale it so if we're
have a flow tree for the selection area
size and some missionary size is the
current Mouse or opposition minus the
start position and then we just set the
welcome scale in here and when we press
on the mouse button we also need to
locate it so in there we scale it and in
here we set the position so the position
won't be the start position all right
that you do it let's test okay here we
are let's see press the mouse button
drag in any up there's our nice
selection area right there
okay so as you can see in here it's not
selecting any and there yep we sound
like one there's like another one and
select both okay awesome so everything
is working correctly alright so with our
visual selection area working let's
actually sound like there are units the
way we're going to select them is with a
very simple tag component so let's go in
here simply make a public struct this
won't be the unit
weapon and it's a I component down here
this is just a simple tag so we're not
going to add any fields and now down
here when we are locating our units
inside the selection area all we need to
do is add our tag component so we're
going to the post update commands to add
a component to this entity add the
unit's selected component alright that
you do it
let's test here we have our two units
let's try something just this one okay
now let's look at the entity debugger
and here we are and you can see that
this one does not have and this one does
have the unit selected component okay
awesome
now that we have our selected component
being added let's make a system in order
to actually visually see it so in here
let's make a public we're going to call
this the unit selected renderer and it's
a component system now in here we cycle
through all the units that have the
selected components
so we are cycling through entities that
have the unit selected component and
grabbing their translation and now in
here let's just do a simple draw mesh so
we do a graphic start draw mesh now in
here we need a mesh and material so
let's add that into our main script so
here we add a material for the unit
selected circle material and also a mesh
okay now for the mesh we can meet the
mesh through code so in here we create
the mesh
already have a nice function to create
just that it takes a mesh width and a
mesh height so let's try some values
here is the create mesh function as you
can see it's very simple it just creates
a simple quad through code so we have
four vertices for UVs and our triangles
and similarly creates a mesh and assign
them so very simple code all right now
let's take care of our material so here
we make a new material this won't be the
selected circle material and over here I
have a nice sprite which is a circle so
we can use just that and here for the
color
let's tint it in green alright great so
this is our material now let's drag the
references here on the game Handler and
we just need to drag the selected circle
material and the mesh is created through
code okay so we have our references for
the things we need now back into the
control system and here we can out
render that so we grab the instance grab
the mesh for the position let's pass in
translation dot value returning that
identity since we don't want rotation
now for the material
and finally I'm layer 0 alright so we
have our very simple unit selected
brender which is going to draw mesh on
top of all the units that are selected
so let's see okay here we are and none
of them is life did something like
nothing and if nothing happens now some
like this one any of there you go
there's a sound like the mesh now we
obviously need to move it and sort it
correctly but yep the mesh is being
drunk and if we sound like this one yep
there you go also in there ok awesome ok
again let's see and yep now it looks
much better
it's behind it and yep it's correctly
place ok great now however we do have
one tiny issue in here when we select
the unit we can already selected so
right now if I select nothing it should
decent like these two but it currently
doesn't so let's solve that now we can
solve this in a very very simple way in
here let's go when we release our mouse
one position come only two positions
okay and in here before we do our
selection we can simply deselect
everything so in here we just cycle
through entities with the unit selected
component when we do it for each ok so
in here we are cycling through all the
entities that have the unit select
component in order to deselect it all we
need to do is actually remove the
component so again go into the post
update commands in order to remove the
component of type in its selected and
that's it
so first we deselect everything on
Whitney so first we deselect every
single entity and then we sound like the
ones inside the error
okay let's see okay so here we are now I
can select nothing and yet nothing is
selected now some like this one okay
it's selected now some like nothing and
it should deselect and if there it is it
decent light so I can select decent like
both them decent let's like this one
then this one okay awesome so we have
all our controls correctly working
exactly as intended
now another potential issue we have is
the size of the selection area so right
now our system works great when we want
to drag from A to B so start here and
stop here and yep it's like correctly
however in the current system it's very
difficult to select just one unit so if
I click right on top this one I can't
actually select it I need to make a
small bounding box in order to actually
get it
so when solution to this problem is to
simply check the size of our selection
area and if it's too small we
automatically increase it so let's do
that here in our control system when you
release the mouse we have the in lower
left and upper right positions and now
in here and let's do a simple distance
check so we do if math dot distance
between the lower left and the upper
right if that distance is under a
certain amount so in here a float for
the selection area means size
so if the size the selection area is
under this amount then we want to
increase it so here if the selection
area is too small let's expand it so we
move the lower left position we increase
it okay so we essentially expand the
lower left in the upper right positions
then the rest of our logic works exactly
the same with both of our updated values
so let's see okay so here we are in the
normal selection yep it still works now
if I just click on him yep there you go
it's unlike that one click and select
okay awesome so we no longer need to
create a bounding box in order to select
each other alright now that we have our
unit selection working let's see how we
can give them orders we have the on left
mouse button to select and now we're
going to use the right mouse button in
order to give a move order so again here
on our system let's do a if input get
mouse button down but in this case we
want the right mouse button so button
one if we press the right mouse button
let's give an order to all of our
selected units so we're going to do a
cycle very much like this one so I go
through all the units emitted and in
here we give the order now in this
example as you saw on our archetype in
here we have the move to component this
what's handling a very simple move to so
let's see the component we have a
boolean to see if this unit is moving a
position that it's moving towards any
movement speed then we have this system
which as you can see very simple widget
says distance towards the target
position and if we are too far we move
towards it and if we're wrong right
there we stop moving
okay so extremely simple movement so
let's go and do that so in here we need
our entity then also a reference to
remove two components now we need to do
is set the move to position to be our
mouse were on position
okay that should do it so when we press
the right mouse button we go through
every single unit that is selected and
we tell it to go to the mouse position
let's see okay so here we are and if no
unit is selected I press it right mouse
button now there is only nothing happens
now something like this one now right
click any of there you go we told that
unit to go there and it did indeed go so
some like that one now some like that
one time to go and yep we have our
orders okay awesome so we can now select
our units and give them orders to each
of them so that is working but one issue
we have is regarding overlapping units
so in here if a unit is right on top of
the other one and we saw like both of
them and tell them to go there and they
both go example into the same position
so right now we can no longer actually
separate them because if I click on
there he's going to automatically select
both them now we can solve this
selection problem very simple we test if
we are doing a normal click and if so
we're only going to select a single unit
so in this case we would be able to
select just one push it to the side and
then they would be separate
okay let's do it all we need to go is go
up here so in here we define a vote
let's say the select only one entity and
by default we start at false because by
default we want to smell it mob talk and
only in here when the selection area is
too small then we set it to true so we
select only one entity and in here let's
make him in for the selected entity
counts and we increase it when we select
an entity and we're only going to run
this code if the select only one entity
is false so we are meant to select
multiple or this selected entity count
is under one
so this way when we do a simple plague
we won't select just one entity but when
we do a selection area we don't select
all them let's see okay here we are and
select one yup it works give it a
movement works right down works now some
like both them put them both one on top
of the other and now I can select one
and yep it only selected one since they
are both overlapping if I do a simple
click
then just select one unit so smite them
both they're both on top and yep like
that okay great so that's another simple
problem that we have solved okay so far
so good we have our unit selection and
control now we have some of the
overlapping problem if two units are
overlapping and we click we can now
separate them however we still have the
issue that both of them go exactly into
the same position so here with four
units it becomes quite a bit of problem
if they all go example into the same
position that looks quite bad so we
should really be giving each unit a
different position so we want to
calculate positions around the target
position so that each unit goes to a
different position but near the target
position let's do that here in our
control system let's go down here this
is where we're telling our units to move
now let's generate a list of positions
so in here a list of three let's make
the move position a list and right now
let's just manually make the enlists so
in here the first one will be the target
position so let's make here if all three
for the target position which will be
the mouse world position
so this is our first position and now
let's put a second position right on the
right okay so here we have only sweet
four different positions now when we
tell the unit to go somewhere let's give
it a position from our list so let's
make a rotating index so an int for the
position in index and we start at zero
and in here we give it a position from
the move position list of position index
so that one gets that position and then
we increase our index so index equals
our index plus 1 and grab the remainder
of the move position unless that counts
so if we have more in and selected and
positions in here we simply rotate them
so let's see if the units are need going
to different positions let's select all
four move them all in there and if they
all went to different positions okay
great now here we have 10 units let's
select all of them tell them to move and
yep now we have some overlap since it is
reusing positions okay
so the logic is working and all we
really need is to create more positions
let's make a simple function to
automatically generate positions within
a circle let's make that function so a
private this will return a list of all
three let's call this to get position
list around since we're going to get the
positions around a target position so if
all three for our starting position then
let's receive a float for the distance
and an end for the position count so
we're going to automatically generate
this many positions within this distance
of this position
so we do a cycle through all of the
positions let's complete the angle for
this index so we define a circle between
this number of positions so here let's
get a direction vector based on this
angle so let's make a function to do
just that I'll return a.load 3 so here
we have a function where we take a
vector and we apply an angle to it and
then we can relate the position based on
the start position and then we move
towards the direction by that distance
and then we simply add it to our
positional lists okay so we are
generating as many positions as we want
let's just restart position up here and
now we can use this function let's go in
here in order to get our move position
list so we're going to grab it using our
function for the start position from be
the target position for the distance
let's put 10 units away and position
come let's just generate 5 so let's see
what that works so you should have 5
positions pause our target position in a
circle within 10 units of the target
position okay here we are select the
mount on the mom to go there any of
there you go as you can see it did
generate 5 positions around our target
position all right
so that logic is working now let's push
it one step further in order to generate
in various rings outside of our target
position so this would be the first ring
then we would have another one outside
another one outside
let's do
okay here's our function we take a start
position then an array of ring distances
M array of position counts we start by
adding the start position then we sign
on through all of the Rings and we count
like those positions using our function
so now we can go up here instead of
using this let's use the other version
so we pass in the start position then we
pass in an array of floats for our ring
distance so the first ring won't be ten
units away the second one 20 then 30 and
for the positions first ring we'll have
five positions in the second one ten and
the third one twenty alright so just
like this we are already generating a
lot of different positions now let's see
here's our 10 units and like all of them
I'm about to go here and there they go
any up there you go they all went into
different positions so now I can move
all of them I can select some go in
there so I'm going there and you
everything worked great let's add more
units okay here we have three keys like
all of them tell them all to go right
there and they all go into different
positions so everything looks very nice
and now we have a very nice arm no it's
not like just those like just those just
those any of we have multiple squads in
a nice RTS unit control system now if
you want you could further improve this
by adding some sort of validation based
on pathfinding or just doing some simple
physics free casts so you generate on
positions and then you validate which
ones are valid so here with our full
system we can click insert drag and
select all the units inside a selection
we can also just click on one of them
and move it individual we are giving our
orders by right-clicking on a unit and
telling it to move and all our units
move to positions that are generated
around a target move position so just
like that we have a very nice
RTS unit control system as always you
can download the project files in its
own lease from mentok of Montcalm if you
liked the video subscribe to the channel
for more unity tutorials post any
questions you have in the comments and
I'll do my best answer alright see you
next time
[Music]